#pragma once

#include <memory>
#include <functional>
#include <type_traits>

#include "Containers/Vector.h"
#include "Containers/WeakObjectPtr.h"



template< typename ReturnType, typename... Args>
struct TFunction
{
    virtual ReturnType ExecuteImpl(Args... InArgs) const = 0;

    virtual bool IsValid() {return true;};

    virtual ~TFunction(){}

};

template<typename FunctionPointerType, typename ReturnType, typename... Args>
struct StaticFunction :public TFunction< ReturnType, Args...>
{
    StaticFunction(FunctionPointerType InFunctionPointer)
    {
        FunctionPointer = InFunctionPointer;
    }

    FunctionPointerType FunctionPointer;

    virtual ReturnType ExecuteImpl(Args... InArgs) const override
    {
        return FunctionPointer(InArgs...);
    }
};

//design for lambda
template<typename ReturnType, typename... Args>
struct SSTDFunction :public TFunction< ReturnType, Args...>
{
public:    
    SSTDFunction(std::function<ReturnType(Args...)> InFunction)
    {
        StdFunction=InFunction;
    }

    virtual ReturnType ExecuteImpl(Args... InArgs)const  override
    {
        return StdFunction(InArgs...);
    }
protected:    
    std::function<ReturnType(Args...)> StdFunction;
};


template<typename FunctionPointerType, typename OwnerType, typename ReturnType, typename... Args>
struct MemberFunction :public TFunction< ReturnType, Args...>
{
    MemberFunction(FunctionPointerType InFunctionPointer, OwnerType* InOwner)
    {
        FunctionPointer = InFunctionPointer;
        Owner=InOwner;
    }

    FunctionPointerType FunctionPointer;
    OwnerType* Owner;

    virtual ReturnType ExecuteImpl(Args... InArgs) const override
    {
        return (Owner->*FunctionPointer)(InArgs...);
    }
};


template<typename FunctionPointerType, typename OwnerType, typename ReturnType, typename... Args>
struct STDSmartPointerMemberFunction :public TFunction< ReturnType, Args...>
{
    STDSmartPointerMemberFunction(FunctionPointerType InFunctionPointer, std::weak_ptr<OwnerType> InOwner)
    {
        FunctionPointer = InFunctionPointer;
        Owner=InOwner;
    }

    FunctionPointerType FunctionPointer;
    std::weak_ptr<OwnerType> Owner;

    virtual ReturnType ExecuteImpl(Args... InArgs) override
    {
        assert(!Owner.expired());
        return (Owner->*FunctionPointer)(InArgs...);
    }

    virtual bool IsValid() override
    { 
         return !Owner.expired();
    };
};

template<typename FunctionPointerType, typename OwnerType, typename ReturnType, typename... Args>
struct SmartPointerMemberFunction :public TFunction< ReturnType, Args...>
{
    SmartPointerMemberFunction(FunctionPointerType InFunctionPointer, TWeakObjectPtr<OwnerType> InOwner)
    {
        FunctionPointer = InFunctionPointer;
        Owner=InOwner;
    }

    FunctionPointerType FunctionPointer;
    TWeakObjectPtr<OwnerType> Owner;

    virtual ReturnType ExecuteImpl(Args... InArgs)const  override
    {
        assert(Owner.IsValid());
        return ((Owner.Get())->*FunctionPointer)(InArgs...);
    }

    virtual bool IsValid() override
    { 
         return Owner.IsValid();
    };
};

//use a weak pointer make sure lambda will not
//invoked after the owner is destroyed
template<typename OwnerType, typename... Args>
struct SLambdaWithOwnerFunction :public TFunction< void, Args...>
{
    SLambdaWithOwnerFunction(std::function<void (Args...)> InFunctionPointer, TWeakObjectPtr<OwnerType> InOwner)
    {
        FunctionPointer = InFunctionPointer;
        Owner=InOwner;
    }

    std::function<void(Args...)> FunctionPointer;
    TWeakObjectPtr<OwnerType> Owner;

    virtual void ExecuteImpl(Args... InArgs) const override
    {
        return FunctionPointer(InArgs...);
    }

    virtual bool IsValid() override
    { 
         return Owner.IsValid();
    };
};


// namespace Function
// {
//    template< typename ReturnType, typename... Args>
//    static TFunction< ReturnType, Args...>* Create(ReturnType (*InRawFunction)(Args...) )
//     {
//         return new StaticFunction<decltype(InRawFunction), ReturnType, Args...>(InRawFunction);
//     }

//     template<typename OwnerType,typename ReturnType, typename... Args>
//     static TFunction< ReturnType, Args...>* Create(OwnerType* InOwner, ReturnType(OwnerType::*InRawFunction)(Args...))
//     {
//         return new MemberFunction<decltype(InRawFunction), OwnerType, ReturnType, Args...>(InRawFunction, InOwner);
//     }

//     template<typename OwnerType,typename ReturnType, typename... Args>
//     static TFunction< ReturnType, Args...>* Create(std::weak_ptr<OwnerType> InOwner, ReturnType(OwnerType::*InRawFunction)(Args...))
//     {
//         return new STDSmartPointerMemberFunction<decltype(InRawFunction), OwnerType, ReturnType, Args...>(InRawFunction, InOwner);
//     }

//     template<typename ReturnType, typename... Args>
//     static TFunction< ReturnType, Args...>* Create(std::function<ReturnType(Args...)> InLambdaFunction)
//     {
//         return new SSTDFunction<ReturnType, Args...>(InLambdaFunction);
//     }

//     template<typename OwnerType,typename ReturnType, typename... Args>
//     static TFunction< ReturnType, Args...>* Create(std::shared_ptr<OwnerType> InOwner, ReturnType(OwnerType::*InRawFunction)(Args...))
//     {
//         return new STDSmartPointerMemberFunction<decltype(InRawFunction), OwnerType, ReturnType, Args...>(InRawFunction, InOwner);
//     }

//     template<typename OwnerType,typename ReturnType, typename... Args>
//     static TFunction< ReturnType, Args...>* Create(TWeakObjectPtr<OwnerType> InOwner, ReturnType(OwnerType::*InRawFunction)(Args...))
//     {
//        return new SmartPointerMemberFunction<decltype(InRawFunction), OwnerType, ReturnType, Args...>(InRawFunction, InOwner);
//     }

// }


template< typename ReturnType, typename... Args>
struct TDelegate
{
    std::shared_ptr<TFunction< ReturnType, Args...>> Function;

    TDelegate(){};

    TDelegate(const TDelegate & Other )
    {
        Function=Other.Function;
    }

    TDelegate(std::nullptr_t )
    {
        Function=nullptr;
    }

    //enable conversion from lambda
    template<typename LambdaType>
    requires (std::is_invocable_r_v<ReturnType,LambdaType,Args...>)
    TDelegate(LambdaType InLambda)
    {
        Bind(std::function<ReturnType(Args...)>(InLambda));
    }

    TDelegate& operator = (std::nullptr_t)
    {
        Function=nullptr;
        return *this;
    }

    bool operator == (std::nullptr_t) const
    {
        return !Function;
    }

    bool operator == (const TDelegate& Other) const
    {
        return Function==Other.Function;
    }

    bool IsBound() const
    {
        return (bool)Function  && Function->IsValid();
    }

    ReturnType Execute(Args... InArgs) const
    {
        return Function->ExecuteImpl(InArgs...);
    }

    bool ExecuteIfBound(Args... InArgs) const
    {
        if (!IsBound())
        {
            return false;
        }
        Function->ExecuteImpl(std::forward<Args>(InArgs)...);

        return true;
    }

    void Unbind()
    {
        Function=nullptr;
    }

    void Bind(std::function<ReturnType(Args...)> InFunction)
    {
      TFunction< ReturnType, Args...>* STDFunction = new SSTDFunction<ReturnType,Args...>(InFunction);
       Function = std::unique_ptr<TFunction< ReturnType, Args...>> (STDFunction);
    }


    // void Bind(ReturnType (*InRawFunction)(Args...) )
    // {
    //     TFunction< ReturnType, Args...>* RawFunction=new StaticFunction<decltype(InRawFunction), ReturnType, Args...>(InRawFunction);
    //     Function = std::unique_ptr<TFunction< ReturnType, Args...>> (RawFunction);
    // }

    template<typename... BindParameterType >
    static TDelegate<ReturnType ,Args... > Create(BindParameterType... BindParameters)
    {
        TDelegate<ReturnType ,Args... > Ret;
        Ret.Bind(BindParameters...);
        return Ret;
    }

    template<typename OwnerType>
    requires (!std::is_base_of_v<RObject,OwnerType>)
    void Bind(OwnerType* InOwner, ReturnType(OwnerType::*InRawFunction)(Args...))
    {
        TFunction< ReturnType, Args...>* RawFunction = new MemberFunction<decltype(InRawFunction), OwnerType, ReturnType, Args...>(InRawFunction, InOwner);
        Function = std::unique_ptr<TFunction< ReturnType, Args...>>(RawFunction);
    }

    template<typename OwnerType>
    void Bind(std::weak_ptr<OwnerType> InOwner, ReturnType(OwnerType::*InRawFunction)(Args...))
    {
        TFunction< ReturnType, Args...>* RawFunction = new STDSmartPointerMemberFunction<decltype(InRawFunction), OwnerType, ReturnType, Args...>(InRawFunction, InOwner);
        Function = std::unique_ptr<TFunction< ReturnType, Args...>>(RawFunction);
    }

    template<typename OwnerType>
    void Bind(std::shared_ptr<OwnerType> InOwner, ReturnType(OwnerType::*InRawFunction)(Args...))
    {
        TFunction< ReturnType, Args...>* RawFunction = new STDSmartPointerMemberFunction<decltype(InRawFunction), OwnerType, ReturnType, Args...>(InRawFunction, InOwner);
        Function = std::unique_ptr<TFunction< ReturnType, Args...>>(RawFunction);
    }

    template<typename OwnerType>
    void Bind(TWeakObjectPtr<OwnerType> InOwner, ReturnType(OwnerType::*InRawFunction)(Args...))
    {
        TFunction< ReturnType, Args...>* RawFunction = new SmartPointerMemberFunction<decltype(InRawFunction), OwnerType, ReturnType, Args...>(InRawFunction, InOwner);
        Function = std::unique_ptr<TFunction< ReturnType, Args...>>(RawFunction);
    }
    
    template<typename OwnerType>
    requires std::is_base_of_v<RObject,OwnerType>
    void Bind(OwnerType* InOwner, ReturnType(OwnerType::*InRawFunction)(Args...))
    {
        TFunction< ReturnType, Args...>* RawFunction = new SmartPointerMemberFunction<decltype(InRawFunction), OwnerType, ReturnType, Args...>(InRawFunction, TWeakObjectPtr<OwnerType>(InOwner));
        Function = std::unique_ptr<TFunction< ReturnType, Args...>>(RawFunction);
    }

    template<typename OwnerType>
    void Bind(TSharedObjectPtr<OwnerType> InOwner, ReturnType(OwnerType::*InRawFunction)(Args...))
    {
        TFunction< ReturnType, Args...>* RawFunction = new SmartPointerMemberFunction<decltype(InRawFunction), OwnerType, ReturnType, Args...>(InRawFunction, TWeakObjectPtr<OwnerType>(InOwner));
        Function = std::unique_ptr<TFunction< ReturnType, Args...>>(RawFunction);
    } 
    
    
};



template<typename... Args>
struct TMultiCastDelegate
{
public:
    using Binding= TFunction< void, Args...>*;

    void Broadcast(Args... InArgs)
    {
        TVector<Binding> ToRemove;

        //use a copy of functions, because the function may removed/or added into 
        //Functions when ExecuteImpl is called
        auto TempFunctions=Functions;
        for(auto& Function:TempFunctions)
        {
            if(Function->IsValid())
            {
                Function->ExecuteImpl(InArgs...);
            }
            else
            {
                ToRemove.Add(Function);
            }
        }

        for(auto& Function:ToRemove)
        {
            Functions.Remove(Function);
            delete Function;
        }
    }

    Binding Add(TFunction< void, Args...>* RawFunction)
    {
        Functions.Add(RawFunction);
        return RawFunction;
    }

    Binding Add(std::function<void (Args...)> InFunction)
    {
        TFunction< void, Args...>* STDFunction=new SSTDFunction<void,Args...>(InFunction);
        Functions.Add(STDFunction);
        return STDFunction;
    }

    
    template<typename OwnerType>
    Binding Add(TWeakObjectPtr<OwnerType> InOwner, std::function<void (Args...)> InFunction)
    {
        TFunction< void, Args...>* Function=new SLambdaWithOwnerFunction<OwnerType,Args...>(InFunction, InOwner);
        Functions.Add(Function);
        return Function;
    }

    template<typename OwnerType>
    requires (!std::is_base_of_v<RObject,OwnerType>)
    Binding Add(OwnerType* InOwner, void(OwnerType::*InRawFunction)(Args...))
    {
        TFunction< void, Args...>* RawFunction = new MemberFunction<decltype(InRawFunction), OwnerType, void, Args...>(InRawFunction, InOwner);
        Functions.Add(RawFunction);
        return RawFunction;
    }

    template<typename OwnerType>
    Binding Add(std::weak_ptr<OwnerType> InOwner, void(OwnerType::*InRawFunction)(Args...))
    {
        TFunction< void, Args...>* RawFunction = new STDSmartPointerMemberFunction<decltype(InRawFunction), OwnerType, void, Args...>(InRawFunction, InOwner);
        Functions.Add(RawFunction);
        return RawFunction;
    }

    template<typename OwnerType>
    Binding Add(std::shared_ptr<OwnerType> InOwner, void(OwnerType::*InRawFunction)(Args...))
    {
        TFunction< void, Args...>* RawFunction = new STDSmartPointerMemberFunction<decltype(InRawFunction), OwnerType, void, Args...>(InRawFunction, InOwner);
        Functions.Add(RawFunction);
        return RawFunction;
    }

    template<typename OwnerType>
    requires std::is_base_of_v<RObject,OwnerType>
    Binding Add(OwnerType* InOwner, void(OwnerType::*InRawFunction)(Args...))
    {
        TFunction< void, Args...>* RawFunction = new SmartPointerMemberFunction<decltype(InRawFunction), OwnerType, void, Args...>(InRawFunction, TWeakObjectPtr<OwnerType>(InOwner));
        Functions.Add(RawFunction);
        return RawFunction;
    }

    template<typename OwnerType>
    Binding Add(TWeakObjectPtr<OwnerType> InOwner, void(OwnerType::*InRawFunction)(Args...))
    {
        TFunction< void, Args...>* RawFunction = new SmartPointerMemberFunction<decltype(InRawFunction), OwnerType, void, Args...>(InRawFunction, InOwner);
        Functions.Add(RawFunction);
        return RawFunction;
    }

    template<typename OwnerType>
    Binding Add(TSharedObjectPtr<OwnerType> InOwner, void(OwnerType::*InRawFunction)(Args...))
    {
        TFunction< void, Args...>* RawFunction = new SmartPointerMemberFunction<decltype(InRawFunction), OwnerType, void, Args...>(InRawFunction, InOwner);
        Functions.Add(RawFunction);
        return RawFunction;        
    }

    void Remove(Binding InFunction)
    {
        Functions.Remove(InFunction);
        delete InFunction;
    }

protected:
    TVector<TFunction< void, Args...>*> Functions;
};